Skip to content

feat(core): define module registry and lifecycle hooks#1925

Merged
kaialang merged 3 commits intotopic/kaialang-eng-1063-open-core-module-system-v1from
kaialang/mod-1-introduce-pluggable-module-registry-and-lifecycle-hooks
Jul 18, 2025
Merged

feat(core): define module registry and lifecycle hooks#1925
kaialang merged 3 commits intotopic/kaialang-eng-1063-open-core-module-system-v1from
kaialang/mod-1-introduce-pluggable-module-registry-and-lifecycle-hooks

Conversation

@kaialang
Copy link
Copy Markdown
Contributor

@kaialang kaialang commented Jun 2, 2025

Motivation and Context

This PR lays the groundwork for the open core module system (ADR: https://github.com/wundergraph/cosmo/pull/1863/files) by introducing:

Core Module system

  • Option-based module registration, sorted by priority
  • Rich module context and structured error handling
  • Module lifecycle management with Provision and Cleanup

Hook system

  • Application Lifecycle: Start/Stop hooks for application-level events
  • GraphQL Server Lifecycle: Server start/stop hooks
  • Router Lifecycle: Request/Response hooks for router-level processing
  • Subgraph Lifecycle: Request/Response hooks for subgraph interactions
  • Operation Lifecycle: Parse (Pre/Post), Normalize (Pre/Post), Validate (Pre/Post), Plan (Pre/Post), Execute (Pre/Post)

Dedicated Context for each hook

  • Interface based Context exposed at every hook for easier API evolution
  • Every Context extends RequestContext for shared context

Utilities

  • OrderedSet (ordered_set.go)
  • Ptr Helper

Tests

  • Unit tests for modules and hooks
  • Integration tests with module lifecycle

Usage Example

//Use module
router, err := core.NewRouter(
     core.WithCustomModulesV1(authModule, metricsModule),
)

// Implement a module
type MyModule struct {
    config *Config
    db     *Database
}

func (m *MyModule) Module() ModuleV1Info {
    return ModuleV1Info{
        ID:       "my-awesome-module",
        Priority: utils.Ptr(10), // execution priority
        New: func() ModuleV1 {
            return &MyModule{}
        },
    }
}

func (m *MyModule) Provision(ctx *ModuleV1Context) error {
    ctx.Logger.Info("Initializing module", zap.String("id", ctx.Module.Module().ID))
    m.db = setupDatabase()
    return nil
}

func (m *MyModule) Cleanup(ctx *ModuleV1Context) error {
    ctx.Logger.Info("Cleaning up module")
    return m.db.Close()
}

func (m *MyModule) OnRouterRequest(ctx RouterRequestHookContext) error {
   // ....
    return nil
}

Followups
Add the exact contract of the first pair of hooks and invoke in router execution. Add integration test for it.

Checklist

Summary by CodeRabbit

Release Notes

  • New Features

    • Added a new modular plugin system (ModulesV1) with lifecycle management and hook support for advanced customization and extension.
    • Introduced configuration options to register custom ModulesV1.
    • Enhanced router lifecycle to initialize and clean up ModulesV1 alongside existing modules.
    • Added a simulated database module with connection pooling and query simulation for testing purposes.
    • Introduced a comprehensive lifecycle hook system covering application, GraphQL server, router, subgraph, and operation phases for extensibility.
  • Bug Fixes

    • Improved error handling and reporting during module provisioning, cleanup, and hook execution.
  • Tests

    • Expanded test coverage for module registration, lifecycle, error handling, hook execution, and integration with GraphQL queries to ensure reliability and prevent regressions.
  • Chores

    • Added utility types and functions for ordered sets and pointer creation to support the new module system.

@github-actions github-actions Bot added the router label Jun 2, 2025
@github-actions
Copy link
Copy Markdown

github-actions Bot commented Jun 2, 2025

Router image scan passed

✅ No security vulnerabilities found in image:

ghcr.io/wundergraph/cosmo/router:sha-c9f6a142131419297b4db7b60467e87d6914b9b0

@kaialang kaialang force-pushed the kaialang/mod-1-introduce-pluggable-module-registry-and-lifecycle-hooks branch from de5ad92 to 3be03ff Compare June 2, 2025 23:49
@kaialang kaialang requested a review from StarpTech June 2, 2025 23:57
Comment thread router/core/modules_v1.go Outdated
Comment thread router/core/modules_v1.go Outdated
Comment thread router/core/modules_v1.go Outdated
Comment thread router/core/hooks.go Outdated
Comment thread router/core/modules_v1_test.go Outdated
Comment thread router/core/modules_v1.go Outdated
@kaialang kaialang force-pushed the kaialang/mod-1-introduce-pluggable-module-registry-and-lifecycle-hooks branch 3 times, most recently from 98b8b8d to 364fedc Compare June 17, 2025 20:18
@kaialang kaialang force-pushed the kaialang/mod-1-introduce-pluggable-module-registry-and-lifecycle-hooks branch from 364fedc to 6ab8955 Compare July 1, 2025 22:36
@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented Jul 1, 2025

Important

Review skipped

Auto reviews are disabled on base/target branches other than the default branch.

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Walkthrough

This change introduces a new modular plugin system ("ModulesV1") to the router framework, enabling registration, prioritization, lifecycle management, and hook-based extensibility for modules. It adds core infrastructure for module registration, ordered execution of lifecycle hooks, and context interfaces for each hook phase. Supporting utilities such as a generic ordered set and pointer helper are included. Comprehensive tests validate module lifecycles, error handling, and hook execution. Integration with the router ensures modules are initialized and cleaned up alongside existing components.

Changes

Files / Areas Change Summary
router/core/hooks.go
router/core/hooks_context.go
Introduces a comprehensive, type-safe hook system for router and operation lifecycles, with interfaces and context types for each phase (application, server, router, subgraph, operation parse/normalize/validate/plan/execute, pre/post). Includes a registry for ordered hook registration and execution with error handling.
router/core/modules_v1.go
router/core/modules_v1_test.go
Implements a thread-safe module registration and lifecycle system ("ModulesV1"), including module info, priorities, provisioning/cleanup, hook registration, and error types. Adds extensive tests for registration, sorting, lifecycle, error propagation, and hook execution.
router/core/router.go
router/core/router_config.go
Integrates ModulesV1 into the router: adds fields for custom and global modules, initializes and cleans up modules during bootstrap and shutdown, and provides an option to inject custom ModulesV1. Updates config struct to support customModulesV1.
router-tests/modules_v1/custom_modules/database_module.go Adds a sample custom module (DatabaseModule) implementing the new ModuleV1 interface, with logging in its Provision and Cleanup methods.
router-tests/modules_v1/module_lifecycle_test.go Adds a test verifying end-to-end lifecycle of a custom ModuleV1, ensuring no regression in GraphQL query behavior when the module system is used.
router/core/hooks_test.go Adds tests for the hook execution system, covering success, error propagation, and empty hook lists.
router/internal/utils/ordered_set.go Introduces a generic OrderedSet type with methods for adding, removing, checking, and retrieving ordered unique elements.
router/internal/utils/ptrs.go Adds a generic Ptr helper function returning a pointer to any value.

Possibly related PRs


Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share
🪧 Tips

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    • I pushed a fix in commit <commit_id>, please review it.
    • Explain this complex logic.
    • Open a follow-up GitHub issue for this discussion.
  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbitai in a new review comment at the desired location with your query. Examples:
    • @coderabbitai explain this code block.
    • @coderabbitai modularize this function.
  • PR comments: Tag @coderabbitai in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.
    • @coderabbitai read src/utils.ts and explain its main purpose.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.
    • @coderabbitai help me debug CodeRabbit configuration file.

Support

Need help? Create a ticket on our support page for assistance with any issues or questions.

Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments.

CodeRabbit Commands (Invoked using PR comments)

  • @coderabbitai pause to pause the reviews on a PR.
  • @coderabbitai resume to resume the paused reviews.
  • @coderabbitai review to trigger an incremental review. This is useful when automatic reviews are disabled for the repository.
  • @coderabbitai full review to do a full review from scratch and review all the files again.
  • @coderabbitai summary to regenerate the summary of the PR.
  • @coderabbitai generate docstrings to generate docstrings for this PR.
  • @coderabbitai generate sequence diagram to generate a sequence diagram of the changes in this PR.
  • @coderabbitai resolve resolve all the CodeRabbit review comments.
  • @coderabbitai configuration to show the current CodeRabbit configuration for the repository.
  • @coderabbitai help to get help.

Other keywords and placeholders

  • Add @coderabbitai ignore anywhere in the PR description to prevent this PR from being reviewed.
  • Add @coderabbitai summary to generate the high-level summary at a specific location in the PR description.
  • Add @coderabbitai anywhere in the PR title to generate the title automatically.

CodeRabbit Configuration File (.coderabbit.yaml)

  • You can programmatically configure CodeRabbit by adding a .coderabbit.yaml file to the root of your repository.
  • Please see the configuration documentation for more information.
  • If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: # yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json

Documentation and Community

  • Visit our Documentation for detailed information on how to use CodeRabbit.
  • Join our Discord Community to get help, request features, and share feedback.
  • Follow us on X/Twitter for updates and announcements.

@kaialang kaialang changed the base branch from topic/kaialang-eng-1063-open-core-module-system-v1 to main July 1, 2025 23:57
@kaialang kaialang force-pushed the kaialang/mod-1-introduce-pluggable-module-registry-and-lifecycle-hooks branch from 4dfae18 to 1b1e5f3 Compare July 2, 2025 01:28
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

♻️ Duplicate comments (3)
router/core/modules_v1.go (3)

45-61: Clean interface design with proper context support!

The ModuleV1 interface and context maintain feature parity with the legacy system while providing a cleaner API. Good that both Provision and Cleanup receive context for timeout control.


136-172: Comprehensive module initialization!

The initialization properly provisions modules, registers all hook types, and provides detailed logging with module IDs and timing information.


174-188: Robust cleanup with error aggregation!

The cleanup implementation ensures all modules get a chance to clean up their resources, properly aggregating any errors that occur.

🧹 Nitpick comments (3)
router/internal/utils/ordered_set.go (1)

25-36: Consider using copy() to avoid potential slice reallocation.

The current implementation works correctly, but using copy() to shift elements left is more efficient as it avoids potential slice reallocation.

 func (s *OrderedSet[T]) Remove(elem T) {
 	if _, exists := s.index[elem]; exists {
 		delete(s.index, elem)
 		// rebuild slice without the removed element
 		for i, v := range s.elements {
 			if v == elem {
-				s.elements = append(s.elements[:i], s.elements[i+1:]...)
+				copy(s.elements[i:], s.elements[i+1:])
+				s.elements = s.elements[:len(s.elements)-1]
 				break
 			}
 		}
 	}
 }
router/core/hooks_context.go (1)

3-3: Enhance the comment to be more descriptive.

The comment could better describe the purpose and design pattern.

-// context interface for every hook
+// Hook context interfaces provide type-safe parameters for lifecycle hooks.
+// Each interface embeds RequestContext to provide access to common request data.
router/core/hooks.go (1)

138-144: Fix typo in comment.

 // moduleHook is a wrapper around a hook that includes the module ID.
-// this is used for tracability in case of hook execution errors.
+// this is used for traceability in case of hook execution errors.
 type moduleHook[H any] struct {
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 2647f84 and 1b1e5f3.

📒 Files selected for processing (11)
  • router-tests/modules_v1/custom_modules/database_module.go (1 hunks)
  • router-tests/modules_v1/module_lifecycle_test.go (1 hunks)
  • router/core/hooks.go (1 hunks)
  • router/core/hooks_context.go (1 hunks)
  • router/core/hooks_test.go (1 hunks)
  • router/core/modules_v1.go (1 hunks)
  • router/core/modules_v1_test.go (1 hunks)
  • router/core/router.go (5 hunks)
  • router/core/router_config.go (1 hunks)
  • router/internal/utils/ordered_set.go (1 hunks)
  • router/internal/utils/ptrs.go (1 hunks)
🧰 Additional context used
🧬 Code Graph Analysis (4)
router/core/router_config.go (1)
router/core/modules_v1.go (1)
  • ModuleV1 (53-61)
router-tests/modules_v1/custom_modules/database_module.go (1)
router/core/modules_v1.go (3)
  • ModuleV1Info (32-43)
  • ModuleV1 (53-61)
  • ModuleV1Context (47-51)
router/core/hooks_context.go (1)
router/core/context.go (1)
  • RequestContext (66-142)
router/core/hooks.go (2)
router/core/hooks_context.go (18)
  • ApplicationStartHookContext (4-6)
  • ApplicationStopHookContext (8-10)
  • GraphQLServerStartHookContext (12-14)
  • GraphQLServerStopHookContext (16-18)
  • RouterRequestHookContext (20-22)
  • RouterResponseHookContext (24-26)
  • SubgraphRequestHookContext (28-30)
  • SubgraphResponseHookContext (32-34)
  • OperationPreParseHookContext (36-38)
  • OperationPostParseHookContext (40-42)
  • OperationPreNormalizeHookContext (44-46)
  • OperationPostNormalizeHookContext (48-50)
  • OperationPreValidateHookContext (52-54)
  • OperationPostValidateHookContext (56-58)
  • OperationPrePlanHookContext (60-62)
  • OperationPostPlanHookContext (64-66)
  • OperationPreExecuteHookContext (68-70)
  • OperationPostExecuteHookContext (72-74)
router/internal/utils/ordered_set.go (2)
  • OrderedSet (3-6)
  • NewOrderedSet (9-14)
⏰ Context from checks skipped due to timeout of 90000ms (10)
  • GitHub Check: build-router
  • GitHub Check: build_test
  • GitHub Check: integration_test (./telemetry)
  • GitHub Check: integration_test (./. ./fuzzquery ./lifecycle ./modules)
  • GitHub Check: build_push_image
  • GitHub Check: image_scan
  • GitHub Check: integration_test (./events)
  • GitHub Check: build_test
  • GitHub Check: Analyze (go)
  • GitHub Check: Analyze (javascript-typescript)
🔇 Additional comments (32)
router/internal/utils/ptrs.go (1)

3-5: LGTM! Clean generic pointer utility implementation.

The generic Ptr function is well-implemented and follows Go conventions. This utility will be helpful for creating pointers to values throughout the codebase, especially in the context of the new ModulesV1 system.

router/core/router_config.go (1)

104-104: LGTM! Proper integration of ModulesV1 support in configuration.

The addition of customModulesV1 field follows the existing pattern and naming convention. This enables passing custom ModuleV1 instances to the router configuration, which aligns perfectly with the new modular plugin system objectives.

router/core/hooks_test.go (2)

13-27: LGTM! Well-designed mock implementation for testing.

The mockHook interface and mockHookImpl struct provide a clean abstraction for testing hook execution. The ability to simulate failures through the shouldErr flag enables comprehensive error testing scenarios.


29-70: LGTM! Comprehensive test coverage for hook execution.

The test suite covers all critical scenarios:

  • Success path with multiple hooks
  • Error handling and message formatting when hooks fail
  • Edge case with empty hook lists

The error message assertion on line 58 validates that the error formatting includes both module ID and hook type, which is crucial for debugging in production.

router-tests/modules_v1/module_lifecycle_test.go (1)

14-36: LGTM! Effective integration test for module lifecycle.

This integration test appropriately validates that the new ModulesV1 system works end-to-end without causing regressions. The test:

  • Uses a real DatabaseModule instance
  • Executes an actual GraphQL query
  • Verifies expected response data
  • Ensures the module lifecycle (provision/cleanup) doesn't interfere with normal operations

The parallel execution and use of testenv framework follows the project's testing patterns.

router-tests/modules_v1/custom_modules/database_module.go (3)

7-7: LGTM! Clean struct definition for the example module.

The empty struct is appropriate for this example module that doesn't need to maintain state.


9-18: LGTM! Proper ModuleV1Info implementation.

The Module() method correctly implements the interface requirements:

  • Unique module ID "database_module"
  • Priority value of 2 for execution ordering
  • Factory function that creates new instances

The use of a pointer to the priority value aligns with the interface design allowing for optional priority setting.


20-28: LGTM! Clean lifecycle method implementations.

Both Provision() and Cleanup() methods properly:

  • Accept the correct ModuleV1Context parameter
  • Use the provided logger for visibility
  • Return nil errors for this simple example
  • Follow the lifecycle contract defined in the ModuleV1 interface

The logging statements provide good visibility into when the module lifecycle events occur, which will be helpful for debugging and monitoring.

router/internal/utils/ordered_set.go (6)

9-14: LGTM!

The constructor properly initializes both the slice and map fields.


17-22: LGTM!

The Add method correctly maintains both uniqueness and insertion order.


39-42: LGTM!

Efficient O(1) membership check using the map.


44-50: LGTM!

Good defensive programming - returning a copy prevents external modification of the internal state.


52-55: LGTM!

Simple and correct length method.


57-61: LGTM!

Properly resets both the slice and map by creating new instances.

router/core/router.go (5)

86-86: LGTM!

Properly typed field for managing ModulesV1 lifecycle hooks.


644-662: LGTM!

The method correctly combines custom and globally registered ModulesV1, with proper early return when no modules are present.


939-942: LGTM!

ModulesV1 initialization is properly integrated into the bootstrap sequence with appropriate error handling.


1498-1508: LGTM!

ModulesV1 cleanup is properly integrated with concurrent execution and appropriate error handling. The context propagation ensures cleanup respects the shutdown timeout.


1763-1767: LGTM!

The option function follows the established pattern for router configuration.

router/core/modules_v1_test.go (5)

14-83: Well-designed test modules!

The test modules effectively cover various scenarios including error conditions, multiple hook implementations, and panic cases. The interface guards provide compile-time verification.


84-151: Comprehensive registration tests!

The tests thoroughly cover all registration scenarios including edge cases and the important hook deduplication behavior when a module implements overlapping interfaces.


153-218: Thorough priority sorting tests!

The tests correctly verify that modules are sorted by priority with nil priorities treated as lowest, and registration order is preserved for equal priorities.


247-279: Good cleanup error aggregation test!

The test properly verifies that all modules get cleaned up regardless of individual failures, and that errors are correctly aggregated.


281-409: Excellent error handling test coverage!

The tests thoroughly verify error propagation, wrapping with module context, and proper error messages for all lifecycle phases.

router/core/hooks_context.go (1)

4-74: Well-designed hook context interfaces!

The interfaces follow a consistent pattern and provide excellent type safety and extensibility for the hook system. The separation of contexts for each lifecycle phase allows for future enhancements without breaking existing implementations.

router/core/modules_v1.go (3)

15-31: Thread-safe registry with proper validation!

The module registry implementation correctly handles concurrent access and validates all required fields before registration.

Also applies to: 71-88, 108-118


32-43: Well-designed priority system!

Using a pointer for Priority elegantly distinguishes between explicit zero priority and unset priority. The sorting implementation correctly handles nil values as lowest priority.

Also applies to: 93-106


190-232: Excellent structured error handling!

The ModuleV1Error type provides clear, actionable error messages with proper context about which module and phase failed. The error unwrapping support enables proper error chain inspection.

router/core/hooks.go (4)

8-137: Well-structured hook interface hierarchy.

The interface design follows best practices with clear separation of concerns, allowing modules to implement only the hooks they need. The consistent error handling pattern across all hooks is excellent.


145-205: Excellent use of generics and OrderedSet for hook management.

The hookRegistry design ensures predictable hook execution order while maintaining type safety through generics. The comprehensive coverage of all hook types is thorough.


207-253: Clean and flexible hook registration design.

The generic registerHook helper elegantly handles optional interface implementation through type assertion, allowing modules to implement only the hooks they need. The registration methods are well-organized by lifecycle category.


255-265: Robust hook execution with proper error handling.

The fail-fast approach with detailed error wrapping (module ID + hook name) provides excellent debugging context. The debug logging strikes a good balance for observability.

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 3

🧹 Nitpick comments (1)
router-tests/modules_v1/custom_modules/database_module.go (1)

128-133: Consider tracking goroutines for proper cleanup.

The goroutine launched here could continue running after module cleanup, potentially causing issues. For production modules, consider using a context or wait group to ensure all goroutines complete before cleanup.

For a more robust implementation, track active goroutines:

type DatabaseModule struct {
    // ... existing fields ...
    wg sync.WaitGroup
}

// In SimulateQuery:
m.wg.Add(1)
go func() {
    defer m.wg.Done()
    time.Sleep(10 * time.Millisecond)
    m.mu.Lock()
    m.metrics.ActiveQueries--
    m.mu.Unlock()
}()

// In Cleanup:
m.wg.Wait() // Wait for all queries to complete
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 1b1e5f3 and 49dacb9.

📒 Files selected for processing (6)
  • router-tests/modules_v1/custom_modules/database_module.go (1 hunks)
  • router-tests/modules_v1/module_lifecycle_test.go (1 hunks)
  • router/core/hooks_test.go (1 hunks)
  • router/core/modules_v1.go (1 hunks)
  • router/core/modules_v1_test.go (1 hunks)
  • router/core/router.go (5 hunks)
🚧 Files skipped from review as they are similar to previous changes (3)
  • router/core/hooks_test.go
  • router/core/router.go
  • router/core/modules_v1_test.go
🧰 Additional context used
🧬 Code Graph Analysis (1)
router-tests/modules_v1/custom_modules/database_module.go (1)
router/core/modules_v1.go (3)
  • ModuleV1Info (14-25)
  • ModuleV1 (62-70)
  • ModuleV1Context (29-33)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (10)
  • GitHub Check: build-router
  • GitHub Check: integration_test (./events)
  • GitHub Check: build_push_image
  • GitHub Check: image_scan
  • GitHub Check: integration_test (./. ./fuzzquery ./lifecycle ./modules)
  • GitHub Check: integration_test (./telemetry)
  • GitHub Check: build_test
  • GitHub Check: build_test
  • GitHub Check: Analyze (go)
  • GitHub Check: Analyze (javascript-typescript)
🔇 Additional comments (1)
router/core/modules_v1.go (1)

144-158: Well-implemented cleanup with proper error aggregation.

The cleanup implementation correctly addresses the previous review feedback by ensuring all modules get a chance to cleanup and aggregating errors using errors.Join.

Comment thread router-tests/modules_v1/module_lifecycle_test.go Outdated
Comment thread router-tests/modules_v1/custom_modules/database_module.go
Comment thread router/core/modules_v1.go Outdated
@kaialang kaialang force-pushed the kaialang/mod-1-introduce-pluggable-module-registry-and-lifecycle-hooks branch from 49dacb9 to 0249480 Compare July 9, 2025 21:14
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

♻️ Duplicate comments (3)
router-tests/modules_v1/module_lifecycle_test.go (1)

85-87: Replace hard-coded sleep with proper synchronization.

Using a fixed sleep duration to wait for asynchronous operations is fragile and can lead to flaky tests. Consider polling the metrics until the expected state is reached with a timeout.

-			time.Sleep(20 * time.Millisecond)
-			finalMetrics := dbModule.GetMetrics()
-			assert.Equal(t, 0, finalMetrics.ActiveQueries, "all queries should be completed")
+			// Wait for queries to complete with timeout
+			ctx, cancel := context.WithTimeout(context.Background(), 1*time.Second)
+			defer cancel()
+			
+			for {
+				select {
+				case <-ctx.Done():
+					t.Fatal("timeout waiting for queries to complete")
+				default:
+					finalMetrics := dbModule.GetMetrics()
+					if finalMetrics.ActiveQueries == 0 {
+						assert.Equal(t, 0, finalMetrics.ActiveQueries, "all queries should be completed")
+						return
+					}
+					time.Sleep(1 * time.Millisecond)
+				}
+			}
router-tests/modules_v1/custom_modules/database_module.go (1)

32-41: Factory function should create a new instance.

The New factory function returns the existing instance (m) instead of creating a new DatabaseModule. This violates the expected behavior where each call to New() should return a fresh instance to avoid shared state issues.

 func (m *DatabaseModule) Module() core.ModuleV1Info {
 	priority := 2
 	return core.ModuleV1Info{
 		ID:       "database_module",
 		Priority: &priority,
 		New: func() core.ModuleV1 {
-			return m
+			return &DatabaseModule{}
 		},
 	}
 }
router/core/modules_v1.go (1)

107-107: Remove redundant hook registry creation.

A new hookRegistry is created here but coreModuleHooks already has one from its constructor. This local variable shadows the field and the freshly created registry here is never used since line 138 assigns the same registry back to the field.

 func (c *coreModuleHooks) initCoreModuleHooks(ctx context.Context, modules []ModuleV1Info) error {
-	hookRegistry := newHookRegistry()
 	var instances []ModuleV1
 
 	for _, info := range modules {

Then update the hook registration calls to use c.hookRegistry instead of hookRegistry:

-		hookRegistry.AddApplicationLifecycle(moduleInstance, info.ID)
-		hookRegistry.AddGraphQLServerLifecycle(moduleInstance, info.ID)
-		hookRegistry.AddRouterLifecycle(moduleInstance, info.ID)
-		hookRegistry.AddSubgraphLifecycle(moduleInstance, info.ID)
-		hookRegistry.AddOperationLifecycle(moduleInstance, info.ID)
+		c.hookRegistry.AddApplicationLifecycle(moduleInstance, info.ID)
+		c.hookRegistry.AddGraphQLServerLifecycle(moduleInstance, info.ID)
+		c.hookRegistry.AddRouterLifecycle(moduleInstance, info.ID)
+		c.hookRegistry.AddSubgraphLifecycle(moduleInstance, info.ID)
+		c.hookRegistry.AddOperationLifecycle(moduleInstance, info.ID)

And remove line 138:

-	c.hookRegistry = hookRegistry
🧹 Nitpick comments (1)
router/internal/utils/ordered_set.go (1)

24-36: Document O(n) complexity of Remove operation.

The Remove method has O(n) time complexity due to slice rebuilding. Consider documenting this performance characteristic for users of this data structure.

-// Remove deletes elem from the set if it exists, preserving order of other elements.
+// Remove deletes elem from the set if it exists, preserving order of other elements.
+// This operation has O(n) time complexity due to slice rebuilding.
 func (s *OrderedSet[T]) Remove(elem T) {
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 49dacb9 and 0249480.

📒 Files selected for processing (11)
  • router-tests/modules_v1/custom_modules/database_module.go (1 hunks)
  • router-tests/modules_v1/module_lifecycle_test.go (1 hunks)
  • router/core/hooks.go (1 hunks)
  • router/core/hooks_context.go (1 hunks)
  • router/core/hooks_test.go (1 hunks)
  • router/core/modules_v1.go (1 hunks)
  • router/core/modules_v1_test.go (1 hunks)
  • router/core/router.go (5 hunks)
  • router/core/router_config.go (1 hunks)
  • router/internal/utils/ordered_set.go (1 hunks)
  • router/internal/utils/ptrs.go (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (2)
  • router/internal/utils/ptrs.go
  • router/core/hooks_context.go
🧰 Additional context used
🧬 Code Graph Analysis (1)
router/core/router_config.go (1)
router/core/modules_v1.go (1)
  • ModuleV1 (62-70)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (9)
  • GitHub Check: integration_test (./. ./fuzzquery ./lifecycle ./modules)
  • GitHub Check: build_push_image
  • GitHub Check: integration_test (./telemetry)
  • GitHub Check: integration_test (./events)
  • GitHub Check: build_test
  • GitHub Check: image_scan
  • GitHub Check: build-router
  • GitHub Check: Analyze (javascript-typescript)
  • GitHub Check: Analyze (go)
🔇 Additional comments (10)
router/core/hooks_test.go (1)

1-100: Well-structured hook tests with comprehensive coverage.

The test implementation properly covers hook execution scenarios, error handling, and registration logic with clear test cases and appropriate assertions.

router/core/modules_v1_test.go (1)

1-341: Comprehensive test coverage for ModuleV1 system.

The test file provides thorough coverage of module lifecycle, error handling, and hook execution with well-structured test cases and proper assertions.

router/core/router.go (5)

87-87: LGTM!

The moduleHooks field follows the established pattern alongside the existing modules field.


657-671: LGTM!

The implementation correctly handles module initialization with proper sorting by priority and error propagation.


948-951: LGTM!

The initialization is properly placed after the existing module system and follows consistent error handling patterns.


1507-1517: LGTM!

The cleanup implementation follows the established concurrent cleanup pattern with proper error aggregation.


1772-1776: LGTM!

The option function follows the established pattern for router configuration options.

router-tests/modules_v1/custom_modules/database_module.go (1)

43-146: LGTM!

The module implementation demonstrates good practices:

  • Proper mutex usage for thread-safe access to shared state
  • Comprehensive initialization and cleanup logic
  • Defensive nil checks in getter methods
  • Simulated async behavior in SimulateQuery
router/core/modules_v1.go (1)

14-106: LGTM!

Excellent implementation with:

  • Clear interface documentation including concurrency safety guidelines
  • Proper priority sorting with nil handling
  • Comprehensive error aggregation in cleanup
  • Well-structured error types for debugging

Also applies to: 108-143, 144-202

router/core/hooks.go (1)

1-264: LGTM!

Well-designed hook system with:

  • Comprehensive lifecycle coverage across all router components
  • Type-safe generic implementation with proper traceability
  • Ordered execution ensuring predictable behavior
  • Clear separation of concerns with dedicated interfaces for each hook type

Comment thread router/core/router_config.go
@kaialang kaialang changed the base branch from main to topic/kaialang-eng-1063-open-core-module-system-v1 July 11, 2025 20:43
@kaialang kaialang changed the base branch from topic/kaialang-eng-1063-open-core-module-system-v1 to main July 11, 2025 20:44
@kaialang kaialang requested a review from jensneuse as a code owner July 11, 2025 20:44
@kaialang kaialang changed the base branch from main to topic/kaialang-eng-1063-open-core-module-system-v1 July 11, 2025 20:44
Copy link
Copy Markdown
Contributor

@StarpTech StarpTech left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM

@StarpTech
Copy link
Copy Markdown
Contributor

Please sort out the rebase issue before merging.

@endigma
Copy link
Copy Markdown
Member

endigma commented Jul 12, 2025

Is this meant to be merged into main or topic/kaialang-eng-1063-open-core-module-system-v1?

@kaialang kaialang force-pushed the kaialang/mod-1-introduce-pluggable-module-registry-and-lifecycle-hooks branch from ea37abb to d4d4db5 Compare July 17, 2025 22:26
@kaialang
Copy link
Copy Markdown
Contributor Author

Is this meant to be merged into main or topic/kaialang-eng-1063-open-core-module-system-v1?

It is meant to be merged into the topic branch topic/kaialang-eng-1063-open-core-module-system-v1

@kaialang
Copy link
Copy Markdown
Contributor Author

Please sort out the rebase issue before merging.

The issue is gone after rebasing.

@kaialang kaialang merged commit fb92790 into topic/kaialang-eng-1063-open-core-module-system-v1 Jul 18, 2025
22 checks passed
@kaialang kaialang deleted the kaialang/mod-1-introduce-pluggable-module-registry-and-lifecycle-hooks branch July 18, 2025 23:07
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants